package com.my.fss.controller;

import java.io.*;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import com.alibaba.fastjson.JSONObject;
import com.my.fss.common.controller.BaseController;
import com.my.fss.common.encrypt.DES3;
import com.my.fss.entity.FssApp;
import com.my.fss.entity.FssAppFile;
import com.my.fss.common.entity.Result;
import com.my.fss.common.entity.ResultCode;
import com.my.fss.service.FssAppFileService;
import com.my.fss.service.FssAppService;
import com.my.fss.common.util.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import redis.clients.jedis.Jedis;
import javax.servlet.http.HttpServletResponse;


@CrossOrigin
@RestController
@Scope("prototype")
@RequestMapping("/fss")
public class FssController extends BaseController {


	@Autowired
	private FssAppService fssAppService;
	@Autowired
	private FssAppFileService fssAppFileService;


	/**
	 * - 文件上传
	 *
	 * @param multipartFile		上传文件
	 * @param app_id	应用ID
	 * @param params	RSA密文字符串，解密后为JSON字符串，格式如下：
	 *                  {
	 *                  	"save_path": "/oa/2020/0429",		保存路径，必须以'/'开头，不能以'/'结尾，不能出现'.'等特殊字符，如果用户没有传值，则默认采用'{ak}/yyyy/MM'的值代替
	 *                  	"is_orig_name": true,				是否使用原始文件名进行保存，如果为false，则有系统自动新建文件名，如果为true但出现重名情况，则文件名中以'(n)'补位，直至不重名为止，默认为true
	 *                  	"random": "9jH917",  				必须，6位及以上随机数
	 *                  	"strategy": 0/1/2       			当is_orig_name=true，且出现重名情况时，重新命名文件时的策略
	 *                  										0: 直接覆盖
	 *                  										1: 保留原文件名，并用'(n)'补位
	 *                  										2: 保留原文件名，并用'(6位随机数)'补位
	 *                  										默认strategy=1
	 *                  }
	 * @return
	 *
	 * @author jiaxd  2020/4/29
	 */
	@RequestMapping(value="/uploadFile", method=RequestMethod.POST)
	public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile multipartFile, @RequestParam("app_id") String app_id, @RequestParam("params") String params) {

		logger.info(Thread.currentThread().getId() + "===============================");
		logger.info("开始接收上传文件");
		logger.info(multipartFile.getOriginalFilename());
		logger.info(multipartFile.getSize());
		logger.info(app_id);
		logger.info(params);
		logger.info("---------------------------------------------------------");

		String savePath = null;
		String fileName = null;
		Boolean isOrigName = true;
		IdWorker idWorker = new IdWorker();
		MultiValueMap<String, String> param = new LinkedMultiValueMap();

		try {
			// 判断appId是否为空
			if (StringUtils.isEmpty(app_id)) {
				param.set("mesg", "app_id不能为空");
				return new ResponseEntity<String>("FAIL", param, HttpStatus.NO_CONTENT);
			}
			// 查对象
			FssApp fssApp = fssAppService.queryById(app_id);
			// 参数解密
			String appSecret = fssApp.getAppSecret();
			JSONObject decryptParams = JSONObject.parseObject(DES3.decrypt(params, appSecret));

			// 随机数校验
			if (StringUtils.isEmpty(decryptParams.get("random")) || decryptParams.get("random").toString().length() < 6) {
				param.set("mesg", "随机数不能为空且不小于6位");
				return new ResponseEntity<String>("FAIL", param, HttpStatus.NO_CONTENT);
			}

			/** ysy 20200908 添加获取userId**/
            String userId = "";
			// 判断储存路径是否存在，存在则按指定路径保存，不存在则系统指定路径
			if (!StringUtils.isEmpty(decryptParams.get("savePath"))) {

				savePath = decryptParams.get("savePath").toString();
				// 正则匹配
				boolean matches = savePath.matches("^\\/[a-z0-9\\/\\-]+[a-z0-9]$");
				if (!matches) {
					param.set("mesg", "储存路径不符合规则");
					return new ResponseEntity<String>("FAIL", param, HttpStatus.NO_CONTENT);
				}
				userId = savePath.substring(savePath.lastIndexOf("/") + 1);

			}
			//系统指定路径
			else {

				//设置默认路径{ak}/yyyy/MM,ak来自fss_app数据库
				savePath = "/" + fssApp.getAppKey() + DateUtil.getFormatDate("/yyyy/MMdd");

			}

			// 是否使用原文件名
			if (StringUtils.isEmpty(decryptParams.get("isOrigName"))) {
				isOrigName = (Boolean)decryptParams.get("isOrigName");
			}

			// 获取原文件名
			String originalFilename = multipartFile.getOriginalFilename();
			// 获取文件名后缀
			String ext = originalFilename.substring(originalFilename.lastIndexOf(".") + 1).toLowerCase();

			if (isOrigName) {

				fileName = originalFilename.replaceAll("#", "＃");
				fileName = fileName.replaceAll("\\+", "＋");
				fileName = fileName.replaceAll("&", "＆");
				fileName = fileName.replaceAll("%", "％");
				fileName = fileName.replaceAll("·", "▪");

				// 文件保存路径
				String filePath = env.getProperty("fss.base.dir") + "/" + app_id + savePath + "/" + fileName;

				// 如果使用原名保存，判断是否有重名文件
				File existsFile = new File(filePath);
				if (existsFile.exists()) {

					int strategy = 1;
					if (decryptParams.get("strategy") != null) {
						strategy = Integer.parseInt(decryptParams.get("strategy").toString());
					}

					// 有重名文件,获取重名文件所在文件夹下所有文件
					String nameOnly = fileName.substring(0, fileName.lastIndexOf("."));
					String saveDir = env.getProperty("fss.base.dir") + "/" + app_id + savePath;

					if (strategy == 1) {
						// strategy=1时的重名文件的处理逻辑
						// 当如果当前要保存的文件名nameOnly，是以"(数字)"结尾的，则通过正则找出最大的数字，并用"+1"方式作为文件名
						// 例如：
						//		ABC(45).docx	→	ABC(46).docx
						// 如果文件名nameOnly不是这种结构的，则往下找有没有以"(数字)"结尾的
						// 如果没有，则直接补一个"(1)"，例如：
						//		ABC.docx	→	ABC(1).docx
						// 如果有，则通过正则找出最大的数字，并用"+1"方式作为文件名，例如：
						//		ABC.docx	→	ABC(4).docx		→	ABC(5).docx

						// 获取保存目录下的所有文件名
						List<String> nameList = getFileNameByDir(saveDir);

						// 正则比对，看当前保存文件名是否是以"(数字)"结尾的
						String regex = ".+\\((\\d+)\\)$";
						if (nameOnly.matches(regex)) {
							int maxIndex = 1;
							Pattern pattern = Pattern.compile(regex);
							for (String name : nameList) {
								name = name.substring(0, name.lastIndexOf('.'));
								Matcher matcher = pattern.matcher(name);
								if (matcher.find()) {
									int index = Integer.parseInt(matcher.group(1));
									if (index >= maxIndex) {
										maxIndex = index + 1;
									}
								}
							}
							fileName = nameOnly.substring(0, nameOnly.lastIndexOf('(')) + "(" + maxIndex + ")." + ext;
						}
						// 不是以数字结尾的……
						else {
							int maxIndex = 1;
							Pattern pattern = Pattern.compile(regex);
							for (String name : nameList) {
								name = name.substring(0, name.lastIndexOf('.'));
								Matcher matcher = pattern.matcher(name);
								if (name.startsWith(nameOnly) && matcher.find()) {
									int index = Integer.parseInt(matcher.group(1));
									if (index >= maxIndex) {
										maxIndex = index + 1;
									}
								}
							}
							fileName = nameOnly + "(" + maxIndex + ")." + ext;
						}
					}
					else if(strategy == 2) {
						/**
						 * strategy=2时的重名文件的处理逻辑
						 * 当如果当前要保存的文件名nameOnly，是以"(6位随机数)"结尾的，则用新的6位随机数进行替换
						 * 否则，直接在文件名后面补上"(6位随机数)"
						 */
						String regex = ".+\\([A-Za-z0-9]{6}\\)$";
						if (nameOnly.matches(regex)) {
							fileName = nameOnly.substring(0, nameOnly.lastIndexOf('(')) + "(" + StrUtil.getRandomString(6) + ")." + ext;
						}
						else {
							fileName = nameOnly + "(" + StrUtil.getRandomString(6) + ")." + ext;
						}
					}
					else if(strategy == 0) {
						/**
						 * strategy=0时，直接覆盖原文件
						 */
					}
				}

			}
			else {
				// 不使用原名保存，则生成文件名，文件名为16为随机字符，包含英文大小写几数字
				fileName = StrUtil.getRandomNumberString(16) + "." + ext;
			}

			// 文件最终保存路径
			String filePath = env.getProperty("fss.base.dir") + "/" + app_id + savePath;

			// 创建保存文件夹
			filePath = filePath.replaceAll("\\\\", "/");
			FileUtil.createDir(filePath);

			// 保存文件
			File savefile = new File(filePath + "/" + fileName);
			multipartFile.transferTo(savefile);

			String path = savePath + "/" + fileName;
			String url = path + "?expire=" + StrUtil.getRandomNumberString(11);


			logger.info(Thread.currentThread().getId() + "-------------------------------");
			logger.info("参数变化");
			logger.info(multipartFile.getOriginalFilename());
			logger.info(multipartFile.getSize());
			logger.info(filePath);
			logger.info(fileName);
			logger.info(savefile.length());
			logger.info("---------------------------------------------------------");


			// 数据库保存信息
			FssAppFile appFile = new FssAppFile();
			appFile.setFileId(Long.toString(idWorker.nextId()));
			appFile.setRootFileId("0");
			appFile.setAppId(app_id);
			appFile.setOrigName(multipartFile.getOriginalFilename());
			appFile.setFileName(fileName);
			appFile.setFilePath(filePath);
			appFile.setSuffix(ext);
			appFile.setSize((int)multipartFile.getSize());
			appFile.setVersion(1);
			appFile.setData("1");
			appFile.setAddUserId(userId != "" ? userId : "userInfo");
			appFile.setAddIp(requestUtils.getIp());
			appFile.setIsDel(false);
			fssAppFileService.isSave(appFile);


			// 将文件发布到Redis上
			Jedis jedis = new Jedis(env.getProperty("redis.host"), Integer.parseInt(env.getProperty("redis.port")));
			String value = app_id + ":" + path;
			// 将文件path和url储存到redis
			jedis.set(url, value);
			// 设置key值有效时间为30分钟
			jedis.expire(url, 1800);
			// 组装返回代码
			JSONObject json = new JSONObject();
			json.put("path", savePath+"/"+fileName);
			param.set("data", URLEncoder.encode(json.toJSONString(), "UTF-8"));


			logger.info(Thread.currentThread().getId() + "-------------------------------");
			logger.info("结束");
			logger.info(path);
			logger.info(json.toJSONString());
			logger.info("=========================================================");


			return new ResponseEntity<String>("SUCCESS", param, HttpStatus.OK);

		} catch (InvalidKeyException e) {
			logger.error(e);
			return new ResponseEntity<String>("FAIL", null, HttpStatus.NO_CONTENT);
		} catch (Exception e) {
			logger.error(e);
			return new ResponseEntity<String>("FAIL", null, HttpStatus.NO_CONTENT);
		}

	}


	/**
	 * - 验证文件是否存在
	 *
	 * @param app_id ：4OXeA03pTlJ0o7FJ
	 *        params: {
	 *					 "path": "/cs/2020/0511/1254976884326600704/刘焕鑫(7).txt",	 必须，保存路径，必须以'/'开头，不能以'/'结尾，不能出现'.'等特殊字符
	 *     				"random": "9jH917"						                     必须，6位及以上随机数
	 *        }
	 *
	 * @author jiaxd  2020/5/6
	 */
	@RequestMapping("/existsFile")
	public Result existsFile(@RequestParam("app_id") String app_id, @RequestParam("params") String params) {

		try {

			if (StringUtils.isEmpty(app_id)) {
				return new Result(ResultCode.FAIL, "应用ID不能为空");
			}
			FssApp fssApp = fssAppService.queryById(app_id);
			String appSecret = fssApp.getAppSecret();
			JSONObject decryptParams = JSONObject.parseObject(DES3.decrypt(params, appSecret));
			if (StringUtils.isEmpty(decryptParams.get("path"))) {
				return new Result(ResultCode.FAIL, "文件路径不能为空");
			}
			if (StringUtils.isEmpty(decryptParams.get("random")) && decryptParams.get("random").toString().length() < 6) {
				return new Result(ResultCode.FAIL, "随机值不能为空且不小于6位");
			}
//			String savePath = Configure.FSS_BASE_DIR + "/" + app_id + decryptParams.get("path");
			String savePath = env.getProperty("fss.base.dir") + "/" + app_id + decryptParams.get("path");
			File file = new File(savePath);
			Map<String, Object> resMap = new HashMap();
			if (file.exists()) {
				resMap.put("exists", true);
			} else {
				resMap.put("exists", false);
			}
			return new Result(ResultCode.SUCCESS, resMap);
		} catch (InvalidKeyException e) {
			return new Result(ResultCode.FAIL, "解密失败");
		} catch (Exception e) {
			logger.error(e);
			return new Result(ResultCode.FAIL);
		}

	}


	/**
	 * - 获取文件访问路径
	 *
	 * @param path ：/oa/2020/0506/Oogz7PStvuY3jdOU.doc
	 *
	 * @author jiaxd  2020/5/6
	 */
	@RequestMapping(value = "/getUrl", method = RequestMethod.POST)
	public ResponseEntity<String> getUrl(@RequestParam("path") String path, @RequestParam("app_id") String app_id) {

		MultiValueMap<String, String> param = new LinkedMultiValueMap();

		try {
			if (StringUtils.isEmpty(path)) {
				param.set("mesg", "文件保存路径不能为空");
				return new ResponseEntity<String>("FAIL", param, HttpStatus.NO_CONTENT);
			}
			if (StringUtils.isEmpty(app_id)) {
				param.set("mesg", "应用ID不能为空");
				return new ResponseEntity<String>("FAIL", param, HttpStatus.NO_CONTENT);
			}
			// 判断文件是否存在
//			String savePath = Configure.FSS_BASE_DIR + "/" + app_id + path;
			String savePath = env.getProperty("fss.base.dir") + "/" + app_id + path;
			File file = new File(savePath);
			boolean exists = file.exists();
			if (exists) {
				//如果存在生成新url并发布至redis
				String url = path + "?expire=" + StrUtil.getRandomNumberString(11);
//				String fullUrl = Configure.FSS_BASE_URL + url;
				String fullUrl = env.getProperty("fss.base.url") + url;
				String value = app_id + ":" + path;
				Jedis jedis = new Jedis(env.getProperty("redis.host"), Integer.parseInt(env.getProperty("redis.port")));
				jedis.set(url, value);
				jedis.expire(url, 1800);
				JSONObject json = new JSONObject();
				json.put("fullURL", fullUrl);
				json.put("savePath", savePath);
				param.set("data", URLEncoder.encode(json.toJSONString(), "UTF-8"));
				System.out.println(fullUrl);
				return new ResponseEntity<String>("SUCCESS", param, HttpStatus.OK);
			} else {
				param.set("data",  URLEncoder.encode("文件不存在", "UTF-8"));
				return new ResponseEntity<String>("FAIL", param, HttpStatus.NO_CONTENT);
			}
		} catch (Exception e) {
			logger.error(e);
			return new ResponseEntity<String>("FAIL", null, HttpStatus.NO_CONTENT);
		}


	}


	/**
	 * - 批量下载文件
	 *
	 * @param app_id ：/oa/2020/0506/Oogz7PStvuY3jdOU.doc
	 *
	 * @author jiaxd  2020/5/6
	 */
	@RequestMapping(value = "/batchDownload")
	public void batchDownload(HttpServletResponse response, @RequestParam("app_id") String app_id, @RequestParam("params") String params) {

		if (StringUtils.isEmpty(app_id)) {
//			return new Result(ResultCode.FAIL, "应用ID不能为空");
			return;
		}

		try {
			// 解密参数
			FssApp fssApp = fssAppService.queryById(app_id);
			String appSecret = fssApp.getAppSecret();
			JSONObject decryptParams = JSONObject.parseObject(DES3.decrypt(params, appSecret));
			if (StringUtils.isEmpty(decryptParams.get("pathList"))) {
				return;
			}
			// 验证请求在规定时间内有效
			if (StringUtils.isEmpty(decryptParams.get("timestamp"))) {
				return;
			} else {
				long timestamp = Long.parseLong(decryptParams.get("timestamp").toString());
				if (System.currentTimeMillis() - timestamp > 1000 * 60) {
					return;
				}
			}

			String[] pathList = ((List<String>) decryptParams.get("pathList")).toArray(new String[((List<String>) decryptParams.get("pathList")).size()]);
			response.setContentType("multipart/form-data");
			response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("附件.zip", "utf-8"));

			OutputStream outputStream = response.getOutputStream();
			ZipOutputStream zipOut = new ZipOutputStream(outputStream);

			byte[] buffer = new byte[1024];
			for (int i = 0; i < pathList.length; i++) {
				BufferedInputStream in = null;
				String path = pathList[i];
				String savePath = env.getProperty("fss.base.dir") + "/" + app_id + path;
				File file = new File(savePath);
				FileInputStream fis = new FileInputStream(file);
				in = new BufferedInputStream(fis);
				zipOut.putNextEntry(new ZipEntry(file.getName()));
				int len;
				while ((len = in.read(buffer)) > 0) {
					zipOut.write(buffer, 0 , len);
				}
				zipOut.closeEntry();
				in.close();
				fis.close();
				//调用函数
			}
		} catch (InvalidKeyException e) {
			logger.error(e);
		} catch (Exception e) {
			logger.error(e);
		}

	}

	/**
	 * 获取文件夹下所有文件名称
	 * 如果fileDir是文件，则只返回它自己
	 * 否则返回目录下的所有文件的文件名
	 *
	 * @param fileDir
	 * @return
	 */
	public List<String> getFileNameByDir(String fileDir) {
		List<String> resultList = new ArrayList();
		try {
			File dir = new File(fileDir);
			if (!dir.isDirectory()) {
				resultList.add(dir.getName());
				return resultList;
			}

			String[] filelist = dir.list();
			for (String files : filelist) {
				File file = new File(fileDir + File.separator + files);
				if (file.isFile()) {
					resultList.add(file.getName());
				}
			}
		}
		catch (Exception e) {
			logger.error(e);
		}
		return resultList;
	}

	public void compress(ZipOutputStream out, File sourceFile, String base) throws Exception {
		//如果路径为目录（文件夹）
		if(sourceFile.isDirectory()) {
			//取出文件夹中的文件（或子文件夹）
			File[] flist = sourceFile.listFiles();

			if(flist.length==0) {//如果文件夹为空，则只需在目的地zip文件中写入一个目录进入点
				out.putNextEntry(new ZipEntry(base + File.separator));
			} else {//如果文件夹不为空，则递归调用compress,文件夹中的每一个文件（或文件夹）进行压缩
				for(int i=0; i<flist.length; i++) {
					compress(out, flist[i], base+File.separator+flist[i].getName());
				}
			}
		} else {
			out.putNextEntry(new ZipEntry(base));
			FileInputStream fos = new FileInputStream(sourceFile);
			BufferedInputStream bis = new BufferedInputStream(fos);
			int len;

			byte[] buf = new byte[1024];
			while((len=bis.read(buf, 0, 1024)) != -1) {
				out.write(buf, 0, len);
			}
			bis.close();
			fos.close();
		}
	}

	/**
	 * - 获取文件访问路径
	 *
	 * @param path ：/oa/2020/0506/Oogz7PStvuY3jdOU.doc
	 *
	 * @author ysy 2020/9/7
	 */
	@RequestMapping(value = "/getSavePath", method = RequestMethod.POST)
	public ResponseEntity<String> getSavePath(@RequestParam("path") String path, @RequestParam("app_id") String app_id) {

		MultiValueMap<String, String> param = new LinkedMultiValueMap();

		try {
			if (StringUtils.isEmpty(path)) {
				param.set("mesg", "文件保存路径不能为空");
				return new ResponseEntity<String>("FAIL", param, HttpStatus.NO_CONTENT);
			}
			if (StringUtils.isEmpty(app_id)) {
				param.set("mesg", "应用ID不能为空");
				return new ResponseEntity<String>("FAIL", param, HttpStatus.NO_CONTENT);
			}
			// 判断文件是否存在
			String savePath = env.getProperty("fss.base.dir") + "/" + app_id + path;
			File file = new File(savePath);
			boolean exists = file.exists();
			if (exists) {
				JSONObject json = new JSONObject();
				json.put("savePath", savePath);
				param.set("data", URLEncoder.encode(json.toJSONString(), "UTF-8"));
				return new ResponseEntity<String>("SUCCESS", param, HttpStatus.OK);
			} else {
				param.set("data",  URLEncoder.encode("文件不存在", "UTF-8"));
				return new ResponseEntity<String>("FAIL", param, HttpStatus.NO_CONTENT);
			}
		} catch (Exception e) {
			logger.error(e);
			return new ResponseEntity<String>("FAIL", null, HttpStatus.NO_CONTENT);
		}


	}

	/**
	 * - 删除文件
	 *
	 * @param path ：/oa/2020/0506/Oogz7PStvuY3jdOU.doc
	 *
	 * @author ysy 2020/9/7
	 */
	@RequestMapping(value = "/deleteFile", method = RequestMethod.POST)
	public void deleteFile(@RequestParam("path") String path, @RequestParam("app_id") String app_id) {

		try {
			if (StringUtils.isEmpty(path)) {
				return;
			}
			if (StringUtils.isEmpty(app_id)) {
				return;
			}
			// 判断文件是否存在
			String savePath = env.getProperty("fss.base.dir") + "/" + app_id + path;
			File file = new File(savePath);
			boolean exists = file.exists();
			if (exists) {
				file.delete();
			}
		} catch (Exception e) {
			logger.error(e);
		}


	}

    /**
     * 获取指定字符串在目标字符串中第n次出现的位置
     *
     * @author ysy  2020/9/7
     */
    public static int getIndex(String string, int i, String str) {
        Matcher slashMatcher = Pattern.compile(str).matcher(string);
        int mIdx = 0;
        while (slashMatcher.find()) {
            mIdx++;
            //当"/"符号第三次出现的位置
            if (mIdx == i) {
                break;
            }
        }
        return slashMatcher.start();
    }

}




